/**
 * @Author : czx
 * @Date : 2025/5/5
 * @Time :23:29
 */

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Font;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.BasicStroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;

/**
 * 功能：游戏面板<br>
 */
public class GamePanel extends JPanel implements MouseListener,MouseMotionListener,ActionListener
{
    private static final long serialVersionUID = -7450693911538496165L;

    /** 游戏逻辑 */
    private GameLogic gameLogic;

    /** 格子大小 */
    final int spaceSize = 40;

    /** 棋子大小（直径） */
    final int chessSize = 30;

    /** 棋盘X方向格数 */
    final int chessBoradXGrids = 15;

    /** 棋盘Y方向格数 */
    final int chessBoradYGrids = 15;

    /** 棋盘左上角X坐标 */
    final int chessBoradX = 30;

    /** 棋盘左上角Y坐标 */
    final int chessBoradY = 30;

    /** 棋盘宽度 */
    final int chessBoradWidth = spaceSize * (chessBoradXGrids - 1);

    /** 棋盘高度 */
    final int chessBoradHeight = spaceSize * (chessBoradYGrids - 1);

    /** 黑棋标识 */
    final int BLACKCHESS = 1;

    /** 白棋标识 */
    final int WHITECHESS = 2;

    /** 对战方式（0-人机对战，1-人人对战） */
    int fightType ;

    /** 先手选择（0-玩家先手，1-电脑先手） */
    int playFirst ;

    /** 黑白选择（0-玩家执黑，1-玩家执白） */
    int chessColor ;

    /** 电脑棋子颜色 */
    int computerChess = -1 ;

    /** 玩家棋子颜色 */
    int playerChess = -1 ;

    /** 白棋悔棋数 */
    int whiteUndoNum = 3;

    /** 黑棋悔棋数 */
    int blackUndoNum = 3;

    /** 全部下棋信息 */
    List<Map<String,Integer>> listChess = new ArrayList<Map<String,Integer>>();

    /** 白棋下棋信息 */
    List<Map<String,Integer>> listChessWhite = new ArrayList<Map<String,Integer>>();

    /** 黑棋下棋信息 */
    List<Map<String,Integer>> listChessBlack = new ArrayList<Map<String,Integer>>();

    /** 胜利下棋信息（胜利后要将五子成线的那五个棋子用红线连在一起显示） */
    List<Map<String,Integer>> listChessWin = new ArrayList<Map<String,Integer>>();

    /** 落子指示器 */
    Map<String,Integer> mapPointer = new HashMap<String,Integer>();

    /** 判断游戏是否结束（true-结束，false-未结束） */
    boolean isGameOver = true;

    /** 组合框控件 */
    private JComboBox<String> jcb_fightType,jcb_playFirst,jcb_chessColor;

    /** 按钮控件 */
    private JButton jb_new,jb_undo,jb_surrender;

    /** 标签控件 */
    JLabel jlb_blackUndoText,jlb_blackStateText,jlb_whiteUndoText,jlb_whiteStateText;

    /**
     * 功能：构造函数<br>
     */
    public GamePanel()
    {
        //与主窗口大小保持一致，并设置背景色（去掉菜单高度）
        this.setSize(760,620);
        this.setBackground(new Color(209,146,17));

        //右边功能区布局
        this.option();

        //鼠标点击与移动监听
        this.addMouseListener(this);
        this.addMouseMotionListener(this);

        //游戏逻辑
        this.gameLogic = new GameLogic(this);

        //初始化游戏
        this.initGame();

    }

    /**
     * 功能：右边功能区布局<br>
     */
    private void option()
    {
        this.setLayout(null);
        //对战方式
        JLabel jlb_fightType = new JLabel("对战方式：");
        jlb_fightType.setFont(new Font("微软雅黑",Font.PLAIN,12));
        jlb_fightType.setForeground(Color.WHITE);
        jlb_fightType.setBounds(this.chessBoradX + this.chessBoradWidth + 30,chessBoradY,100,24);
        this.add(jlb_fightType);
        this.jcb_fightType = new JComboBox<String>(new String[]{"人机对战","人人对战"});
        this.jcb_fightType.setFont(new Font("微软雅黑",Font.PLAIN,12));
        this.jcb_fightType.setBounds(this.chessBoradX + this.chessBoradWidth + 30,chessBoradY + 30,100,24);
        this.add(this.jcb_fightType);
        //谁先手
        JLabel jlb_playFirst = new JLabel("先手选择：");
        jlb_playFirst.setBounds(this.chessBoradX + this.chessBoradWidth + 30,chessBoradY + 70,100,24);
        jlb_playFirst.setFont(new Font("微软雅黑",Font.PLAIN,12));
        jlb_playFirst.setForeground(Color.WHITE);
        this.add(jlb_playFirst);
        this.jcb_playFirst = new JComboBox<String>(new String[]{"玩家先手","电脑先手"});
        this.jcb_playFirst.setFont(new Font("微软雅黑",Font.PLAIN,12));
        this.jcb_playFirst.setBounds(this.chessBoradX + this.chessBoradWidth + 30,chessBoradY + 100,100,24);
        this.add(this.jcb_playFirst);
        //谁执黑
        JLabel jlb_chessColor = new JLabel("黑白选择：");
        jlb_chessColor.setBounds(this.chessBoradX + this.chessBoradWidth + 30,chessBoradY + 140,100,24);
        jlb_chessColor.setFont(new Font("微软雅黑",Font.PLAIN,12));
        jlb_chessColor.setForeground(Color.WHITE);
        this.add(jlb_chessColor);
        this.jcb_chessColor = new JComboBox<String>(new String[]{"玩家执黑","玩家执白"});
        this.jcb_chessColor.setFont(new Font("微软雅黑",Font.PLAIN,12));
        this.jcb_chessColor.setBounds(this.chessBoradX + this.chessBoradWidth + 30,chessBoradY + 170,100,24);
        this.add(this.jcb_chessColor);
        //按钮
        this.jb_new = new JButton("开始游戏");
        this.jb_new.setFont(new Font("微软雅黑",Font.PLAIN,12));
        this.jb_new.setBounds(this.chessBoradX + this.chessBoradWidth + 30, chessBoradY + 230,100,30);
        this.jb_new.setActionCommand("newGame");
        this.jb_new.addActionListener(this);
        this.add(this.jb_new);
        this.jb_undo = new JButton("我要悔棋");
        this.jb_undo.setFont(new Font("微软雅黑",Font.PLAIN,12));
        this.jb_undo.setBounds(this.chessBoradX + this.chessBoradWidth + 30, chessBoradY + 280,100,30);
        this.jb_undo.setActionCommand("undo");
        this.jb_undo.addActionListener(this);
        this.jb_undo.setEnabled(false);
        this.add(this.jb_undo);
        this.jb_surrender = new JButton("我认输了");
        this.jb_surrender.setFont(new Font("微软雅黑",Font.PLAIN,12));
        this.jb_surrender.setBounds(this.chessBoradX + this.chessBoradWidth + 30, chessBoradY + 330,100,30);
        this.jb_surrender.setActionCommand("surrender");
        this.jb_surrender.addActionListener(this);
        this.jb_surrender.setEnabled(false);
        this.add(this.jb_surrender);
        //黑棋提示
        JPanel groupBoxBlack = new JPanel();
        groupBoxBlack.setLayout(null);
        groupBoxBlack.setBackground(this.getBackground());
        groupBoxBlack.setBounds(this.chessBoradX + this.chessBoradWidth + 30, chessBoradY + 380,100,80);
        groupBoxBlack.setBorder(BorderFactory.createTitledBorder("黑棋"));
        this.add(groupBoxBlack);
        JLabel jlb_blackUndo = new JLabel("悔棋：");
        jlb_blackUndo.setFont(new Font("微软雅黑",Font.PLAIN,12));
        jlb_blackUndo.setForeground(Color.WHITE);
        jlb_blackUndo.setBounds(10,16,40,30);
        groupBoxBlack.add(jlb_blackUndo);
        this.jlb_blackUndoText = new JLabel("剩"+Integer.toString(this.blackUndoNum)+"次");
        this.jlb_blackUndoText.setFont(new Font("微软雅黑",Font.BOLD,12));
        this.jlb_blackUndoText.setForeground(Color.darkGray);
        this.jlb_blackUndoText.setBounds(44,16,50,30);
        groupBoxBlack.add(this.jlb_blackUndoText);
        JLabel jlb_blackState = new JLabel("状态：");
        jlb_blackState.setFont(new Font("微软雅黑",Font.PLAIN,12));
        jlb_blackState.setForeground(Color.WHITE);
        jlb_blackState.setBounds(10,44,40,30);
        groupBoxBlack.add(jlb_blackState);
        this.jlb_blackStateText = new JLabel("未开始");
        this.jlb_blackStateText.setFont(new Font("微软雅黑",Font.BOLD,12));
        this.jlb_blackStateText.setForeground(Color.darkGray);
        this.jlb_blackStateText.setBounds(44,44,50,30);
        groupBoxBlack.add(this.jlb_blackStateText);
        //白棋提示
        JPanel groupBoxWhite = new JPanel();
        groupBoxWhite.setLayout(null);
        groupBoxWhite.setBackground(this.getBackground());
        groupBoxWhite.setBounds(this.chessBoradX + this.chessBoradWidth + 30, chessBoradY + 480,100,80);
        groupBoxWhite.setBorder(BorderFactory.createTitledBorder("白棋"));
        this.add(groupBoxWhite);
        JLabel jlb_whiteUndo = new JLabel("悔棋：");
        jlb_whiteUndo.setFont(new Font("微软雅黑",Font.PLAIN,12));
        jlb_whiteUndo.setForeground(Color.WHITE);
        jlb_whiteUndo.setBounds(10,16,40,30);
        groupBoxWhite.add(jlb_whiteUndo);
        this.jlb_whiteUndoText = new JLabel("剩"+Integer.toString(this.whiteUndoNum)+"次");
        this.jlb_whiteUndoText.setFont(new Font("微软雅黑",Font.BOLD,12));
        this.jlb_whiteUndoText.setForeground(Color.darkGray);
        this.jlb_whiteUndoText.setBounds(44,16,50,30);
        groupBoxWhite.add(this.jlb_whiteUndoText);
        JLabel jlb_whiteState = new JLabel("状态：");
        jlb_whiteState.setFont(new Font("微软雅黑",Font.PLAIN,12));
        jlb_whiteState.setForeground(Color.WHITE);
        jlb_whiteState.setBounds(10,44,40,30);
        groupBoxWhite.add(jlb_whiteState);
        this.jlb_whiteStateText = new JLabel("未开始");
        this.jlb_whiteStateText.setFont(new Font("微软雅黑",Font.BOLD,12));
        this.jlb_whiteStateText.setForeground(Color.darkGray);
        this.jlb_whiteStateText.setBounds(44,44,50,30);
        groupBoxWhite.add(this.jlb_whiteStateText);

    }

    /**
     * 功能：初始化游戏<br>
     */
    public void initGame()
    {
        this.isGameOver = true;
        //清空下棋列表
        this.listChess.clear();
        this.listChessBlack.clear();
        this.listChessWhite.clear();
        this.listChessWin.clear();
        //落子指示器初始化
        this.mapPointer.put("x",-1);
        this.mapPointer.put("y",-1);
        this.mapPointer.put("show",0);	//0-不显示，1-显示
        //对战方式
        if("人人对战".equals(this.jcb_fightType.getSelectedItem().toString()))
        {
            this.fightType = 1;
        }
        else
        {
            this.fightType = 0;
        }
        //先手选择
        if("电脑先手".equals(this.jcb_playFirst.getSelectedItem().toString()))
        {
            this.playFirst = 1;
        }
        else
        {
            this.playFirst = 0;
        }
        //黑白选择
        if("玩家执白".equals(this.jcb_chessColor.getSelectedItem().toString()))
        {
            this.chessColor = 1;
        }
        else
        {
            this.chessColor = 0;
        }
        //电脑与玩家棋子颜色
        if(this.fightType == 0)
        {
            if(this.chessColor == 1)
            {
                this.playerChess = this.WHITECHESS;
                this.computerChess = this.BLACKCHESS;
            }
            else
            {
                this.computerChess = this.WHITECHESS;
                this.playerChess = this.BLACKCHESS;
            }
        }
        //悔棋数初始化
        this.whiteUndoNum = 3;
        this.blackUndoNum = 3;
        //设置控件状态
        this.setComponentState(false);
    }

    /**
     * 功能：设置控件状态<br>
     * 参数：true-新开局；false-未开局<br>
     */
    public void setComponentState(boolean _flag)
    {
        if(_flag)	//新游戏已经开始了
        {
            this.jcb_fightType.setEnabled(false);
            this.jcb_playFirst.setEnabled(false);
            this.jcb_chessColor.setEnabled(false);
            this.jb_new.setEnabled(false);
            this.jb_undo.setEnabled(true);
            this.jb_surrender.setEnabled(true);
        }
        else	//新游戏还未开始
        {
            this.jcb_fightType.setEnabled(true);
            this.jcb_playFirst.setEnabled(true);
            this.jcb_chessColor.setEnabled(true);
            this.jb_new.setEnabled(true);
            this.jb_undo.setEnabled(false);
            this.jb_surrender.setEnabled(false);
        }
    }

    /**
     * 功能：绘图<br>
     */
    @Override
    public void paint(Graphics g)
    {
        //调用父类,让其做一些事前的工作，如刷新屏幕等
        super.paint(g);

        //清屏
        g.setColor(this.getBackground());
        g.fillRect(this.chessBoradX,this.chessBoradY,this.chessBoradWidth,this.chessBoradHeight);

        //因为要画一些特殊效果，所以要用Graphics2D
        Graphics2D g2D = (Graphics2D)g;

        //开始画棋盘（五子棋盘由15条横线与15条竖线交叉形成，共225个点）
        //String[] tip = {" 0"," 1"," 2"," 3"," 4"," 5"," 6"," 7"," 8"," 9","10","11","12","13","14"};	//横竖线的坐标，有利于编程是查看定位
        g2D.setColor(Color.BLACK);
        g.setFont(new Font("微软雅黑",Font.PLAIN,14));
        for(int i=0;i<this.chessBoradXGrids;i++)
        {
            g2D.drawLine(this.chessBoradX,this.chessBoradY + i * this.spaceSize,this.chessBoradX + this.chessBoradWidth,this.chessBoradY + i * this.spaceSize);
            g2D.drawLine(this.chessBoradX + i * this.spaceSize,this.chessBoradY,this.chessBoradX + i * this.spaceSize,this.chessBoradY + this.chessBoradHeight);
            //g2D.drawString(tip[i],this.chessBoradX - 20,this.chessBoradY + 4 + i * this.spaceSize);
            //g2D.drawString(tip[i],this.chessBoradX - 2 + i * this.spaceSize,this.chessBoradY - 10);
        }

        //画5个定位点
        g2D.fillArc(this.chessBoradX + 3 * this.spaceSize - 4,this.chessBoradY + 3 * this.spaceSize - 4,8,8,0,360);
        g2D.fillArc(this.chessBoradX + 11 * this.spaceSize - 4,this.chessBoradY + 3 * this.spaceSize - 4,8,8,0,360);
        g2D.fillArc(this.chessBoradX + 7 * this.spaceSize - 4,this.chessBoradY + 7 * this.spaceSize - 4,8,8,0,360);
        g2D.fillArc(this.chessBoradX + 3 * this.spaceSize - 4,this.chessBoradY + 11 * this.spaceSize - 4,8,8,0,360);
        g2D.fillArc(this.chessBoradX + 11 * this.spaceSize - 4,this.chessBoradY + 11 * this.spaceSize - 4,8,8,0,360);

        //画落子指示器
        if(this.mapPointer.get("show") == 1)
        {
            g2D.setColor(Color.RED);
            g2D.setStroke(new BasicStroke(2.5f));
            //先以交叉点为中心取到指示器周围的4个角坐标
            //中心点坐标
            int x = this.chessBoradX + this.mapPointer.get("x") * this.spaceSize;
            int y = this.chessBoradY + this.mapPointer.get("y") * this.spaceSize;
            //左上角坐标，并向下向右画线
            int x1 = x - this.chessSize / 2;
            int y1 = y - this.chessSize / 2;
            g2D.drawLine(x1,y1,x1,y1 + this.chessSize / 4);
            g2D.drawLine(x1,y1,x1 + this.chessSize / 4,y1);
            //右上角坐标，并向下向左画线
            x1 = x + this.chessSize / 2;
            y1 = y - this.chessSize / 2;
            g2D.drawLine(x1,y1,x1,y1 + this.chessSize / 4);
            g2D.drawLine(x1,y1,x1 - this.chessSize / 4,y1);
            //左下角坐标，并向上向右画线
            x1 = x - this.chessSize / 2;
            y1 = y + this.chessSize / 2;
            g2D.drawLine(x1,y1,x1,y1 - this.chessSize / 4);
            g2D.drawLine(x1,y1,x1 + this.chessSize / 4,y1);
            //右下角坐标，并向上向左画线
            x1 = x + this.chessSize / 2;
            y1 = y + this.chessSize / 2;
            g2D.drawLine(x1,y1,x1,y1 - this.chessSize / 4);
            g2D.drawLine(x1,y1,x1 - this.chessSize / 4,y1);
            //System.out.println("("+this.mapPointer.get("x")+","+this.mapPointer.get("y")+")");
        }

        //画棋子（就是一个黑色的圆，里面在接近圆心的位置上画一个由白到黑的渐变内圆）
        for(int i=0;i<this.listChess.size();i++)
        {
            Map<String,Integer> map = this.listChess.get(i);
            int lineX = map.get("x");
            int lineY = map.get("y");
            int flag = map.get("flag");
            //将垂直与水平线的数目转化为像素
            int x = this.chessBoradX + lineX * this.spaceSize;
            int y = this.chessBoradY + lineY  * this.spaceSize;
            //做一个由白到黑渐变的圆绘画
            RadialGradientPaint paint = null;
            if(flag == this.WHITECHESS)
            {
                paint = new RadialGradientPaint(x + 5,y + 5,90, new float[]{0f,1f},new Color[]{Color.WHITE,Color.BLACK});
            }
            else
            {
                paint = new RadialGradientPaint(x + 3,y + 3,10, new float[]{0f,1f},new Color[]{Color.WHITE,Color.BLACK});
            }
            g2D.setPaint(paint);
            //着色微调
            g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);	//消除画图锯齿
            g2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);		//追求速度或质量
            //画圆，里面套一个白到黑渐变的圆
            Ellipse2D e = new Ellipse2D.Float(x - this.chessSize/2,y - this.chessSize/2,this.chessSize,this.chessSize);
            g2D.fill(e);
            //标记最后一个棋子的红矩形框
            if(i == this.listChess.size() - 1)
            {
                g2D.setColor(Color.RED);
                g2D.setStroke(new BasicStroke(2.5f));
                g2D.drawRect(x - 5,y - 5,10,10);
            }
        }

        //画五子成线
        if(this.listChessWin.size() > 4)
        {
            g2D.setColor(Color.RED);
            g2D.setStroke(new BasicStroke(2.5f));
            int x1 = 0;
            int y1 = 0;
            for(int i=0;i<this.listChessWin.size();i++)
            {
                Map<String,Integer> map = this.listChessWin.get(i);
                if(i == 0)	//先得到第一个点
                {
                    x1 = map.get("x");
                    y1 = map.get("y");
                }
                else	//再用第一个点与其他的点连线
                {
                    g2D.drawLine(this.chessBoradX + x1 * this.spaceSize,this.chessBoradY + y1 * this.spaceSize,this.chessBoradX + map.get("x") * this.spaceSize,this.chessBoradY +map.get("y") * this.spaceSize);
                }
            }
        }

    }

    /**
     * 功能：开始新游戏<br>
     */
    public void newGame()
    {
        //初始化游戏
        this.initGame();
        //设置控件状态
        this.setComponentState(true);
        //设置游戏结束标识
        this.isGameOver = false;
        //电脑先手
        if(this.fightType == 0 && this.playFirst == 1)
        {
            this.gameLogic.computerPlay();
        }

    }

    /**
     * 功能：悔棋<br>
     */
    public void undo()
    {
        if(this.gameLogic.undo()){this.repaint();}
    }

    /**
     * 功能：投降<br>
     */
    public void surrender()
    {
        if(this.isGameOver){return;}
        JOptionPane.showMessageDialog(null,"啥，认输了，还能再有点出息不！");
        this.isGameOver = true;
        this.setComponentState(false);
        this.jlb_blackStateText.setText("已结束");
        this.jlb_whiteStateText.setText("已结束");
    }

    /**
     * 功能：鼠标移动监听<br>
     */
    @Override
    public void mouseMoved(MouseEvent e)
    {
        this.gameLogic.mouseMoved(e);
    }

    /**
     * 功能：鼠标单击监听<br>
     */
    @Override
    public void mouseClicked(MouseEvent e)
    {
        this.gameLogic.mouseClicked(e);
    }

    /**
     * 功能：功能监听<br>
     */
    @Override
    public void actionPerformed(ActionEvent e)
    {
        String command = e.getActionCommand();

        if("newGame".equals(command))
        {
            this.newGame();
        }
        else if("undo".equals(command))
        {
            this.undo();
        }
        else if("surrender".equals(command))
        {
            this.surrender();
        }

    }

    @Override
    public void mousePressed(MouseEvent e) {}

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    @Override
    public void mouseDragged(MouseEvent e) {}

}
